home *** CD-ROM | disk | FTP | other *** search
- /*
- File: OpenGL DrawSprocket.cp
-
- Contains: An example of OpenGL and DrawSprocket integration for full screen drawing.
- Handles multiple displays and timing of swaps per second.
-
- The optimal way to select a rendering context would be to check the available devices, find the best,
- then present the user with a selction dialog only if there are more than one "best" device.
- Currently there is not a good mechanism to do this easily, so the two best other options are
-
- 1) iterate throught the available devices find the best renderer, build a DSp context on this,
- then build your OPenGL context. The downfall of this method is that you know nothing about the graphics
- capabilities of the device and the user can't select another screen. The upside is that you get
- a renderer that is has the capabilities you want.
-
- 2) Allow the user to select a screen to use, build a DSp context and then a OpenGL context. This
- second method is simpler but relies on user knowing which device is the best at 3D.
-
- This sample illustrates the second method which makes it simpler and allows us to illustrate the
- DSp/OpenGL interface well.
-
- Written by: Geoff Stahl
- original code by John Stauffer
-
- Copyright: 1999 Apple Computer, Inc., All Rights Reserved
-
- Change History (most recent first):
-
- <6> 9/14/99 GGS Corrected buffer rect handling and cleaned up code
- <5> 7/14/99 GGS Fixed multi-monitor window centering
- <4> 7/13/99 GGS Add work around for over zealous checking in single buffer DSp context attributes
- <3> 7/5/99 GGS Now correctly handle multi-monitor (DSp front buffer for single device; Window on top of context for multiple devices)
- <2> 5/28/99 GGS Added better multi-monitor support, clean code, corrected blanking bug, added timing, correct pixel formats
- <1> ? ? Initial build
-
- This code can be built for DrawSprocket 1.7. Enable the #define kDSp17 below to add this.
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
-
- */
-
- // Building with DrawSprocket 1.7
- //#define kDSp17 1
-
- #include <Fonts.h>
- #include <sound.h>
- #include <resources.h>
- #include <Folders.h>
- #include <Files.h>
- #include <textutils.h>
-
- #include <DrawSprocket.h>
-
- #include <math.h>
- #include <stdio.h>
- #include <string.h>
-
- #include "gl.h"
- #include "aglRenderers.h"
- #include "agl.h"
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- enum
- {
- kBitsPerPixel = 32,
- kContextWidth = 400,
- kContextHeight = 400
- };
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // globals
- NumVersion gVersionDSp;
- DSpContextAttributes gTheContextAttributes;
- DSpContextReference gTheContext;
- GDHandle ghGD;
-
- const RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 };
- const RGBColor rgbWhite = { 0xFFFF, 0xFFFF, 0xFFFF };
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // function prototypes
- void CToPStr (register const char *inString, StringPtr outString );
- CGrafPtr SetupScreen (GDHandle *hGD, short *numDevices);
- void ShutdownScreen(CGrafPtr *ppWin);
- void aglDebugStr (void);
- AGLContext setupAGL(GDHandle hGD, AGLDrawable win, short numDevices);
- void cleanupAGL(AGLContext ctx);
- void drawGL(AGLContext ctx);
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // main: setup, draw loop, frames per swap timing, clean up (exit with mouse click)
-
- int main(void)
- {
- CGrafPtr pWin;
- GDHandle hGD;
- short numDevices = 0;
- AGLContext ctx;
- CGrafPtr portSave;
- GDHandle hGDSave;
- RGBColor colorSave;
-
- // timing info
- long frames = 0, duration;
- float fps;
- short fNum, len;
- Str255 aStr = "\p";
- char aChar[256];
-
- // Mac Init
- MaxApplZone ();
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(nil);
- InitCursor();
-
- pWin = SetupScreen(&hGD, &numDevices); // Setup DSp for OpenGL
- if(!pWin)
- return 0;
- ctx = setupAGL(hGD, (AGLDrawable) pWin, numDevices); // Setup the OpenGL context
- if(!ctx)
- return 0;
-
- duration = TickCount (); // timing start
-
- do {
- drawGL(ctx); // OGL draw
- frames++;
- } while (!Button());
- glFinish (); // flush and sync
-
- // do frame per swap timing
- duration = TickCount () - duration;
- fps = (float) frames * 60.0 / duration;
- sprintf (aChar, "Swaps/Sec: %0.1f", fps);
- CToPStr (aChar, aStr);
- // set up for screen output
- GetGWorld (&portSave, &hGDSave);
- SetGWorld (pWin, hGD);
- GetForeColor (&colorSave);
- RGBForeColor (&rgbWhite);
- // set text
- GetFNum("\pGeneva", &fNum);
- TextFont(fNum);
- TextSize(12);
- // draw centered to screen
- len = StringWidth (aStr);
- MoveTo (pWin->portRect.left + (pWin->portRect.right - pWin->portRect.left) / 2 - (len / 2), 18);
- DrawString(aStr);
- // wait for click to continue
- while (Button()) {};
- while (!Button()) {};
- // reset colors
- RGBForeColor (&colorSave);
- SetGWorld (portSave, hGDSave);
-
- cleanupAGL(ctx); // Cleanup the OpenGL context
- ShutdownScreen(&pWin); // DSp shutdown
-
- FlushEvents(everyEvent, 0);
- return 0;
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // Copy C string to Pascal string
-
- void CToPStr (register const char *inString, StringPtr outString ) // copies in to out
- {
- register unsigned char x = 0;
- do
- {
- *(((char*)outString) + 1 + x) = *(inString + x);
- x++;
- }
- while ((*(inString + x) != 0) && (x < 256));
- *((char*)outString) = (char) x;
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // Set up DSp screens, handles multi-monitor correctly
-
- CGrafPtr SetupScreen (GDHandle *phGD, short *numDevices)
- {
- GDHandle hDevice;
- CGrafPtr pCGOut;
- DSpContextAttributes foundAttributes;
- DisplayIDType displayID;
- Rect rectWin;
- RGBColor rgbSave;
- GrafPtr pGrafSave;
- *numDevices = 0;
-
- // check for DSp
- if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpStartup)
- {
- DebugStr("\pDSp not installed\n");
- phGD = NULL;
- numDevices = 0;
- return NULL;
- }
-
- #ifdef kDSp17
- if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpGetVersion)
- #endif
- {
- // must be 1.1.4 or earlier so look at extension
- short resFileSave, resFileDSp;
- short vRefDSp;
- long dirIDDSp;
- FSSpec fsSpecDSp;
- OSErr err;
- Handle hVersion;
-
- // assume 1.0 version to start with (worst case)
- gVersionDSp.majorRev = 1;
- gVersionDSp.minorAndBugRev = 0;
- gVersionDSp.stage = 0x80;
- gVersionDSp.nonRelRev = 0;
-
- resFileSave = CurResFile();
- SetResLoad (false);
- // search application directory for debug lib
- resFileDSp = OpenResFile ("\pDrawSprocketDebugLib");
- err = ResError ();
- if (fnfErr == err)
- {
- // search application directory for lib
- resFileDSp = OpenResFile ("\pDrawSprocketLib");
- err = ResError ();
- if (fnfErr == err)
- {
- // search extensions folder for debug lib
- FindFolder (kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &vRefDSp, &dirIDDSp);
- FSMakeFSSpec (vRefDSp, dirIDDSp, "\pDrawSprocketLib", &fsSpecDSp);
- resFileDSp = FSpOpenResFile (&fsSpecDSp, fsRdPerm);
- err = ResError ();
- if (fnfErr == err)
- {
- // search extensions folder directory for lib
- FSMakeFSSpec (vRefDSp, dirIDDSp, "\pDrawSprocketDebugLib", &fsSpecDSp);
- resFileDSp = FSpOpenResFile (&fsSpecDSp, fsRdPerm);
- err = ResError ();
- }
- }
- }
- // if we have an open resource file and no error
- if ((noErr == err) && (-1 != resFileDSp))
- {
- SetResLoad (true);
- hVersion = GetResource ('vers', 1);
- err = ResError ();
- if ((noErr == err) && (NULL != hVersion))
- {
- gVersionDSp.majorRev = *(((unsigned char *)*hVersion) + 0);
- gVersionDSp.minorAndBugRev = *(((unsigned char *)*hVersion) + 1);
- gVersionDSp.stage = *(((unsigned char *)*hVersion) + 2);
- gVersionDSp.nonRelRev = *(((unsigned char *)*hVersion) + 3);
- ReleaseResource (hVersion);
- }
- UseResFile(resFileSave);
- CloseResFile(resFileDSp);
- }
- }
- #ifdef kDSp17
- else
- gVersionDSp = DSpGetVersion ();
- #endif
-
- hDevice = DMGetFirstScreenDevice (true); // check number of screens
- do
- {
- (*numDevices)++;
- hDevice = DMGetNextScreenDevice (hDevice, true);
- }
- while (hDevice);
-
- if(noErr != DSpStartup()) // start DSp and find a good context
- DebugStr("\pUnable to startup\n");
-
- // Note: DSp currently REQUIRES the back buffer attributes even if only one buffer is required
- memset(&gTheContextAttributes, 0, sizeof (DSpContextAttributes));
- gTheContextAttributes.displayWidth = kContextWidth;
- gTheContextAttributes.displayHeight = kContextHeight;
- gTheContextAttributes.colorNeeds = kDSpColorNeeds_Require;
- gTheContextAttributes.displayBestDepth = kBitsPerPixel;
- gTheContextAttributes.backBufferBestDepth = kBitsPerPixel;
- gTheContextAttributes.displayDepthMask = kDSpDepthMask_All;
- gTheContextAttributes.backBufferDepthMask = kDSpDepthMask_All;
- gTheContextAttributes.pageCount = 1; // only the front buffer is needed
-
- // will display user dialog if context selection is required otherwise as find best context
- if (noErr != DSpUserSelectContext(&gTheContextAttributes, 0L, nil, &gTheContext))
- DebugStr("\pA suitable display context could not be found.");
- if (noErr != DSpContext_GetAttributes (gTheContext, &foundAttributes)) // see what we actually found
- DebugStr("\pUnable to find a suitable device\n");
-
- // reset width and height to full screen and handle our own centering
- // HWA will not correctly center less than full screen size contexts
- gTheContextAttributes.displayWidth = foundAttributes.displayWidth;
- gTheContextAttributes.displayHeight = foundAttributes.displayHeight;
- gTheContextAttributes.pageCount = 1; // only the front buffer is needed
- gTheContextAttributes.contextOptions = 0 | kDSpContextOption_DontSyncVBL; // no page flipping and no VBL sync needed
-
- DSpSetBlankingColor(&rgbBlack);
- if (noErr != DSpContext_GetDisplayID(gTheContext, &displayID)) // get our device for future use
- DebugStr("\pDSpContext_GetDisplayID() had an error.");
- if (noErr != DMGetGDeviceByDisplayID(displayID, phGD, false)) // get GDHandle for ID'd device
- DebugStr("\pDMGetGDeviceByDisplayID() had an error.");
- if (noErr != DSpContext_Reserve( gTheContext, &gTheContextAttributes )) // reserve our context
- DebugStr("\pUnable to create the display!");
-
- HideCursor ();
-
- if (noErr != DSpContext_FadeGammaOut (NULL, NULL)) // fade display, remove for debug
- DebugStr("\pUnable to fade the display!");
- if (noErr != DSpContext_SetState (gTheContext, kDSpContextState_Active)) // activate our context
- DebugStr("\pUnable to set the display!");
-
- // create a new window in our context (required for multi-monitor handling)
- // for single monitor we just use the DSp context
- // note: OpenGL is expecting a window so it can enumerate the devices it spans,
- // if you us a CGrafPtr instead it MUST be on the main device and always remain there
- if (*numDevices > 1) // multi-monitor
- {
- // center window in our context's gdevice
- rectWin.top = (***phGD).gdRect.top + ((***phGD).gdRect.bottom - (***phGD).gdRect.top) / 2; // h center
- rectWin.top -= kContextHeight / 2;
- rectWin.left = (***phGD).gdRect.left + ((***phGD).gdRect.right - (***phGD).gdRect.left) / 2; // v center
- rectWin.left -= kContextWidth / 2;
- rectWin.right = rectWin.left + kContextWidth;
- rectWin.bottom = rectWin.top + kContextHeight;
-
- pCGOut = (CGrafPtr)NewCWindow (NULL, &rectWin, "\p", 0, plainDBox, (WindowPtr)-1, 0, 0);
-
- // paint back ground black before fade in to avoid white background flash
- ShowWindow((GrafPtr)pCGOut);
- GetPort (&pGrafSave);
- SetPort ((GrafPtr)pCGOut);
- GetForeColor (&rgbSave);
- RGBForeColor (&rgbBlack);
- PaintRect (&pCGOut->portRect);
- RGBForeColor (&rgbSave); // ensure color is reset for proper blitting
- SetPort (pGrafSave);
- }
- else // single screen (just use front buffer, but must set glviewport and buffer rect)
- if (noErr != DSpContext_GetFrontBuffer( gTheContext, &pCGOut)) // get our buffer to draw into
- DebugStr("\pUnable to get front buffer");
-
- if (noErr != DSpContext_FadeGammaIn (NULL, NULL))
- DebugStr("\pUnable to fade the display!");
-
- return pCGOut;
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // clean up DSp
-
- void ShutdownScreen (CGrafPtr *ppWin)
- {
- DSpContext_FadeGammaOut (NULL, NULL);
- if (*ppWin)
- DisposeWindow ((WindowPtr)*ppWin);
- *ppWin = NULL;
- DSpContext_SetState( gTheContext, kDSpContextState_Inactive );
- DSpContext_FadeGammaIn (NULL, NULL);
- ShowCursor ();
- DSpContext_Release (gTheContext);
- DSpShutdown ();
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // Dump agl errors to debugger string
-
- void aglDebugStr (void)
- {
- Str255 errStr;
- unsigned char * inErrStr = (unsigned char *) aglErrorString(aglGetError());
- short x = 0;
- do
- errStr [++x] = *inErrStr;
- while (*(inErrStr++) && (x < 256));
- errStr [0] = x - 1;
- DebugStr (errStr);
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // OpenGL Setup
-
- AGLContext setupAGL (GDHandle hGD, AGLDrawable win, short numDevices)
- {
- // different possible pixel format choices for different renderers
- // (note: AGL_NO_RECOVERY is added to prevent failure on multi-monitor systems)
- // basics requirements are RGBA and double buffer
- // OpenGL will select acclerated context if available
-
- // software renderer
- // GLint attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_GENERIC_ID, AGL_ALL_RENDERERS, AGL_DOUBLEBUFFER, AGL_NONE };
-
- // any renderer
- GLint attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_ALL_RENDERERS, AGL_DOUBLEBUFFER, AGL_NONE };
-
- // OpenGL compliant HWA renderer
- // GLint attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_ACCELERATED, AGL_DOUBLEBUFFER, AGL_NONE };
-
- // OpenGL compliant ATI renderer
- // GLint attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_ATI_ID, AGL_DOUBLEBUFFER, AGL_NONE };
-
- // OpenGL compliant ATI HWA renderer
- // GLint attrib[] = { AGL_RGBA, AGL_NO_RECOVERY, AGL_RENDERER_ID, AGL_RENDERER_ATI_ID, AGL_ACCELERATED, AGL_DOUBLEBUFFER, AGL_NONE };
-
- GLint bufferPos [4]; // x, y (inverted), w (clip), h (clip)
- AGLPixelFormat fmt;
- AGLContext ctx;
-
- if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) // check for existance of OpenGL
- {
- DebugStr("\pOpenGL not installed\n");
- return NULL;
- }
-
- if (hGD) // Choose an rgb pixel format use a device if we have one
- {
- fmt = aglChoosePixelFormat(&hGD, 1, attrib); // get an appropriate pixel format
- if (NULL == fmt)
- {
- aglDebugStr ();
- return NULL;
- }
- }
- else
- {
- fmt = aglChoosePixelFormat(NULL, 0, attrib); // get an appropriate pixel format
- if (NULL == fmt)
- {
- aglDebugStr ();
- return NULL;
- }
- }
-
- ctx = aglCreateContext (fmt, NULL); // Create an AGL context
- if(NULL == ctx)
- {
- aglDebugStr ();
- return NULL;
- }
-
- if (1 == numDevices) // ensure that the buffer rect is set to limit OpenGL memory requirements
- { // when setting the drawable
- // set buffer rectangle
- bufferPos [0] = win->portRect.left + (win->portRect.right - win->portRect.left) / 2; // h center
- bufferPos [0] -= kContextWidth / 2;
- bufferPos [1] = win->portRect.top + (win->portRect.bottom - win->portRect.top) / 2; // v center
- bufferPos [1] -= kContextHeight / 2;
- bufferPos [2] = kContextWidth ;
- bufferPos [3] = kContextHeight;
- if (!aglSetInteger (ctx, AGL_BUFFER_RECT, bufferPos)) // turn on buffer rect
- {
- aglDebugStr ();
- return NULL;
- }
- if(!aglEnable (ctx, AGL_BUFFER_RECT)) // enable buffer rectangle
- {
- aglDebugStr ();
- return NULL;
- }
- }
-
- if(!aglSetDrawable(ctx, win)) // attach the CGrafPtr to the context
- {
- aglDebugStr ();
- return NULL;
- }
-
- if(!aglSetCurrentContext(ctx)) // make the context the current context
- {
- aglDebugStr ();
- return NULL;
- }
-
- if (numDevices == 1)
- glViewport (0, 0, kContextWidth, kContextHeight); // scale viewport to buffer rectangle size
-
- aglDestroyPixelFormat(fmt); // pixel format is no longer needed
-
- return ctx;
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // OpenGL Cleanup
-
- void cleanupAGL(AGLContext ctx)
- {
- aglSetCurrentContext(NULL);
- aglSetDrawable(ctx, NULL);
- aglDestroyContext(ctx);
- }
-
- //-----------------------------------------------------------------------------------------------------------------------
-
- // OpenGL Drawing
-
- void drawGL(AGLContext ctx)
- {
- static float f, s, c;
- GLboolean fState = GL_FALSE;
-
- f += 0.01;
- s = sin(f);
- c = cos(f);
-
- glClearColor(0.15f, 0.15f, 0.15f, 1.0f); // Clear color buffer to dark grey
- glClear(GL_COLOR_BUFFER_BIT);
-
- glBegin(GL_POLYGON); // Draw a smooth shaded polygon
- glColor3d(1.0, 0.0, 0.0);
- glVertex3d(s, c, 0.0);
- glColor3d(0.0, 1.0, 0.0);
- glVertex3d(-c, s, 0.0);
- glColor3d(0.0, 0.0, 1.0);
- glVertex3d(-s, -c, 0.0);
- glColor3d(0.7, 0.7, 0.7);
- glVertex3d(c, -s, 0.0);
- glEnd();
-
- aglSwapBuffers(ctx); // send swap command
- }
-